import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * 
 */

/**
 * @author Joseph
 *
 */
public class VendingMachineTest {
	VendingMachine vm;
	VendingMachineItem soda, chips, snickers, crackers, itemPurchased;

	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		// setup a new VendingMachine
		vm       = new VendingMachine();
		soda     = new VendingMachineItem("soda", .75);
		chips    = new VendingMachineItem("chips", 1.25);
		snickers = new VendingMachineItem("snickers", 1.55);
		crackers = new VendingMachineItem("crackers", .50);
	}

	/**
	 * @throws java.lang.Exception
	 */
	@After
	public void tearDown() throws Exception {
		vm = null;
	}

	/**
	 * Test method for {@link VendingMachine#addItem(VendingMachineItem, java.lang.String)}.
	 * testing addItem when everything is valid and good
	 */
	@Test
	public void testAddItemAllValid() {
		vm.addItem(soda, VendingMachine.A_CODE);
	}

	/**
	 * Test method for {@link VendingMachine#addItem(VendingMachineItem, java.lang.String)}.
	 * testing addItem when everything is valid and good
	 */
	@Test ( expected = VendingMachineException . class )
	public void testAddItemInvalidSlot() {
		vm.addItem(soda, "invalid slot");
	}

	/**
	 * Test method for {@link VendingMachine#addItem(VendingMachineItem, java.lang.String)}.
	 * testing addItem when everything is valid and good
	 */
	@Test ( expected = VendingMachineException . class )
	public void testAddItemSlotOccupied() {
		vm.addItem(soda, VendingMachine.A_CODE);
		vm.addItem(chips, VendingMachine.A_CODE);
	}

	/**
	 * Test method for {@link VendingMachine#getItem(java.lang.String)}.
	 * testing getItem with an invalid slot
	 */
	@Test ( expected = VendingMachineException . class )
	public void testGetItemInvalidSlot() {
		vm.getItem("E");
	}

	/**
	 * Test method for {@link VendingMachine#getItem(java.lang.String)}.
	 * testing getItem with an empty slot
	 */
	@Test
	public void testGetItemEmptySlot() {
		assertEquals(null, vm.getItem(VendingMachine.A_CODE));
	}

	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 * testing removing a valid item
	 */
	@Test
	public void testRemoveItemValid() {
		vm.addItem(chips, VendingMachine.A_CODE);
		itemPurchased = vm.removeItem(VendingMachine.A_CODE);
		assertEquals(chips, itemPurchased);
		assertEquals(chips.getName(), itemPurchased.getName());
		assertEquals(chips.getPrice(), itemPurchased.getPrice(), 0);
	}

	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 * testing removing from invalid slot
	 */
	@Test ( expected = VendingMachineException . class )
	public void testRemoveItemInvalidSlot() {
		itemPurchased = vm.removeItem("some invalid slot");
	}

	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 * testing removing from unoccupied slot
	 */
	@Test ( expected = VendingMachineException . class )
	public void testRemoveItemSlotUnoccupied() {
		itemPurchased = vm.removeItem(VendingMachine.A_CODE);
	}

	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 * testing removing an item twice
	 */
	@Test ( expected = VendingMachineException . class )
	public void testRemoveItemTwice() {
		vm.addItem(chips, VendingMachine.A_CODE);
		itemPurchased = vm.removeItem(VendingMachine.A_CODE);
		itemPurchased = vm.removeItem(VendingMachine.A_CODE);
	}

	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 * testing adding, removing, re-adding,re-removing 
	 */
	@Test
	public void testRemoveItemAndAddItem() {
		vm.addItem(snickers, VendingMachine.C_CODE);
		itemPurchased = vm.removeItem(VendingMachine.C_CODE);
		assertEquals(snickers, itemPurchased);
		vm.addItem(soda, VendingMachine.C_CODE);
		itemPurchased = vm.removeItem(VendingMachine.C_CODE);
		assertEquals(soda, itemPurchased);
	}

	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 * testing inserting $0.00
	 */
	@Test
	public void testInsertMoneyZero() {
		vm.insertMoney(0);
		assertEquals(0, vm.getBalance(), 0);
	}

	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 * testing inserting a positive amount
	 */
	@Test
	public void testInsertMoneyPositive() {
		vm.insertMoney(1.23);
		assertEquals(1.23, vm.getBalance(), 0);
	}

	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 * testing inserting money twice
	 */
	@Test
	public void testInsertMoneyTwice() {
		assertEquals(0, vm.getBalance(), 0);
		vm.insertMoney(1.23);
		assertEquals(1.23, vm.getBalance(), 0);
		vm.insertMoney(.23);
		assertEquals(1.46, vm.getBalance(), 0);
	}

	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 * testing inserting a negative amount
	 */
	@Test ( expected = VendingMachineException . class )
	public void testInsertMoneyNegative() {
		vm.insertMoney(-1.23);
	}

	/**
	 * Test method for {@link VendingMachine#getBalance()}.
	 * testing getting empty balance
	 */
	@Test
	public void testGetBalanceEmpty() {
		Double balance = vm.getBalance();
		assertEquals(0, balance, 0);
	}

	/**
	 * Test method for {@link VendingMachine#getBalance()}.
	 * testing getting non-empty balance
	 */
	@Test
	public void testGetBalanceNonEmpty() {
		vm.insertMoney(.40);
		Double balance = vm.getBalance();
		assertEquals(.40, balance, 0);
	}

	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * testing makePurchase with zero balance
	 */
	@Test
	public void testMakePurchaseZeroBalance() {
		vm.addItem(snickers, VendingMachine.C_CODE);
		assertEquals(false, vm.makePurchase(VendingMachine.C_CODE));
	}

	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * testing makePurchase with not-enough balance
	 */
	@Test
	public void testMakePurchaseNotEnoughBalance() {
		vm.addItem(snickers, VendingMachine.C_CODE);
		vm.insertMoney(0.25);
		assertEquals(false, vm.makePurchase(VendingMachine.C_CODE));
	}

	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * testing makePurchase with exactly enough balance
	 */
	@Test
	public void testMakePurchaseExactlyEnoughBalance() {
		vm.addItem(snickers, VendingMachine.C_CODE);
		vm.insertMoney(1.55);
		assertEquals(true, vm.makePurchase(VendingMachine.C_CODE));
	}

	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * testing makePurchase with remaining balance
	 */
	@Test
	public void testMakePurchaseExtraBalance() {
		vm.addItem(snickers, VendingMachine.C_CODE);
		vm.insertMoney(1.56);
		assertEquals(true, vm.makePurchase(VendingMachine.C_CODE));
		assertEquals(0.01, vm.getBalance(), 0.1);
	}

	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * testing makePurchase with invalid slot
	 */
	@Test ( expected = VendingMachineException . class )
	public void testMakePurchaseInvalidSlot() {
		assertEquals(true, vm.makePurchase("Some invalid slot"));
	}

	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * testing makePurchase with empty slot
	 */
	@Test
	public void testMakePurchaseEmptySlot() {
		assertEquals(false, vm.makePurchase(VendingMachine.C_CODE));
	}

	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * testing makePurchase with remaining balance
	 */
	@Test
	public void testMakePurchaseTwice() {
		vm.addItem(snickers, VendingMachine.D_CODE);
		vm.insertMoney(1.56);
		assertEquals(true, vm.makePurchase(VendingMachine.D_CODE));
		assertEquals(false, vm.makePurchase(VendingMachine.D_CODE));
	}

	/**
	 * Test method for {@link VendingMachine#returnChange()}.
	 * testing returnChange when balance is zero
	 */
	@Test
	public void testReturnChangeZero() {
		assertEquals(0, vm.returnChange(), 0);
	}

	/**
	 * Test method for {@link VendingMachine#returnChange()}.
	 * testing returnChange when balance > 0
	 */
	@Test
	public void testReturnChangePositive() {
		vm.addItem(snickers, VendingMachine.B_CODE);
		vm.insertMoney(2.00);
		vm.makePurchase(VendingMachine.B_CODE);
		assertEquals(0.45, vm.returnChange(), 0.1);
	}

	/**
	 * Test method for {@link VendingMachine#returnChange()}.
	 * testing returnChange when balance > 0 after purchase
	 */
	@Test
	public void testReturnChangeZeroAfterPurchase() {
		vm.addItem(soda, VendingMachine.B_CODE);
		vm.addItem(crackers, VendingMachine.D_CODE);
		vm.insertMoney(1.25);
		vm.makePurchase(VendingMachine.B_CODE);
		vm.makePurchase(VendingMachine.D_CODE);
		assertEquals(0, vm.returnChange(), 0.1);
	}

}
